﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Axiom.Core;
using Axiom.Graphics;
using Axiom.Input;
using Axiom.Math;
using Axiom.Graphics.Collections;
using Axiom.Configuration;
using System.Xml;
using System.IO;

namespace GameDesigner
{
    public partial class SceneEditorForm : Form
    {
        enum MovementRotationAxe { X, Y, Z };

        const float movementSpeed = 50;
        const string MovementIndicatorName = "AGDMovementIndicator";
        const string AgdSceneNodeName = "AGDSceneNode";
        const string AgdCameraAndCoorSysNodeName = "AGDEditorCameraAndCoordSys";
        const string AgdCameraNodeName = "AGDEditorCamera";
        const string AgdCoorSysNodeName = "AGDEditorCoordSys";
        const string AgdCameraName = "AGDEditorCamera";

        Vector3 velocity = Vector3.Zero;
        InputReader input;
        RenderWindow _renderWindow;
        SceneManager _sceneManager;
        Root AxiomEngine;
        System.Timers.Timer RenderLoopTimer;
        string DescriptorFileAddress;
        TreeNode LastNode;
        Descriptor descriptor;
        bool inMovement = false;
        bool inRotation = false;
        MovementRotationAxe MovementRotationCurrentAxe;

        public SceneEditorForm()
        {
            InitializeComponent();

            this.Left = Screen.PrimaryScreen.WorkingArea.Width - this.Bounds.Width;
            this.Top = 0;

            RenderLoopTimer = new System.Timers.Timer(100);
        }

        public void OpenProject(string FileAddress)
        {
            System.Diagnostics.Debug.Write("Loading project from descriptor file: " + FileAddress);
            if (AxiomEngine != null)
                AxiomEngine.QueueEndRendering();

            #region Axiom Engine Config
            System.Diagnostics.Debug.Write("Configuring Axiom Engine. . .");

            //CreateRoot
            AxiomEngine = Program.AxiomEngine;
            descriptor = new Descriptor(FileAddress);
            DescriptorFileAddress = FileAddress;

            //DefineResources
            descriptor.LoadResourceLocations();
            //add our own resources
            ResourceGroupManager.Instance.AddResourceLocation(
                Path.GetDirectoryName(Application.ExecutablePath) + Path.DirectorySeparatorChar + "EidtorResources",
                "Folder",
                "AGDRes",
                true,
                false
                );

            //Setup Render System
            descriptor.SetupRenderSystem(AxiomEngine);

            //Create Render Window
            _renderWindow = descriptor.CreateRenderWindow(AxiomEngine);

            //Initialize Resource Groups
            descriptor.InitializeResourceGroups();

            //Create Scene
            _sceneManager = descriptor.CreateScene(AxiomEngine);
            _sceneManager.RootSceneNode.CreateChildSceneNode(AgdSceneNodeName);

            //we use our editor's camera don't use the one created with descriptor
            //descriptor.CreateDefaultCamera(_sceneManager, _renderWindow);
            CreateEditorCamera(_sceneManager, _renderWindow);

            //Load World Geometry
            descriptor.LoadWorldGeometry(_sceneManager);

            input = PlatformManager.Instance.CreateInputReader();
            input.Initialize(_renderWindow, true, true, false, false);

            System.Diagnostics.Debug.Write("Axiom Engine configuration finished.");
            #endregion

            #region Load Trees

            HierarchyTreeView.Nodes.Find("RenderSystemTree", false).First().Tag =
                AxiomEngine.RenderSystem;

            LoadSceneNodesTree();
            LoadLights();
            LoadCameras();
            LoadResourceNodes(descriptor);

            //TODO: if scenemanager supports Terrain
            LoadTerrainNodes();
            LoadTerrainConfig();

            #endregion

            System.Diagnostics.Debug.Write("Starting render loop.");
            AxiomEngine.FrameStarted += AxiomListener;
            RenderLoopTimer.Elapsed += AxiomRenderLoop;
            RenderLoopTimer.Enabled = true;
        }

        public Camera CreateEditorCamera(SceneManager TargetSceneManager, RenderWindow window)
        {
            Camera camera = TargetSceneManager.CreateCamera(AgdCameraName);
            //Create viewport
            Viewport viewport = window.AddViewport(camera);
            camera.AspectRatio = (float)(viewport.ActualWidth / viewport.ActualHeight);
            viewport.BackgroundColor = Axiom.Core.ColorEx.Blue;

            //Set camera options
            camera.Position = Vector3.Zero;
            camera.Near = 5f;

            SceneNode camNode = TargetSceneManager.GetSceneNode(AgdSceneNodeName)
                .CreateChildSceneNode(AgdCameraAndCoorSysNodeName)
                .CreateChildSceneNode(AgdCameraNodeName);
            camNode.AttachObject(camera);
            camNode.Pitch(-45f);

            SceneNode coorSysNode = TargetSceneManager.GetSceneNode(AgdCameraAndCoorSysNodeName)
                .CreateChildSceneNode(AgdCoorSysNodeName);

            coorSysNode.Position = new Vector3(-30, -80, -45);
            attach3dCoordSysToSceneNode(coorSysNode);

            return camera;
        }

        private void attach3dCoordSysToSceneNode(SceneNode scNode)
        {
            Entity Axe = _sceneManager.CreateEntity("xAxe", @"EditorMeshes\xAxe.mesh");
            SceneNode AxeNode = scNode.CreateChildSceneNode("xAxeNode");
            AxeNode.Rotate(new Vector3(1, 0, 0), 90);
            AxeNode.Rotate(new Vector3(0, 0, 1), -90);
            AxeNode.AttachObject(Axe);

            Axe = _sceneManager.CreateEntity("yAxe", @"EditorMeshes\yAxe.mesh");
            AxeNode = scNode.CreateChildSceneNode("yAxeNode");
            AxeNode.AttachObject(Axe);

            Axe = _sceneManager.CreateEntity("zAxe", @"EditorMeshes\zAxe.mesh");
            AxeNode = scNode.CreateChildSceneNode("zAxeNode");
            AxeNode.Rotate(new Vector3(1, 0, 0), 90);
            AxeNode.AttachObject(Axe);
        }

        private void SaveProject(string FileAddress)
        {
            //TODO: we would show a dialog to let user select anything that needs to be saved:
            // Terrain
            // SceneNodes

            List<string> igNames = new List<string>();
            igNames.Add(AgdSceneNodeName);
            igNames.Add("TerrainRoot");

            descriptor.SaveToFile(FileAddress, AxiomEngine.RenderSystem.ConfigOptions, _sceneManager.RootSceneNode, igNames);
        }

        /// <summary>
        /// Finds and returns the list of all resources in ResourceTree
        /// </summary>
        /// <returns></returns>
        List<Resource> GetAllResources()
        {
            TreeNode ResTree = HierarchyTreeView.Nodes.Find("ResourcesTree", false).First();

            List<Resource> ress = new List<Resource>(ResTree.Nodes.Count);

            foreach (TreeNode tNode in ResTree.Nodes)
            {
                if (tNode.Tag is Resource)
                    ress.Add((Resource)tNode.Tag);
            }

            return ress;
        }

        #region Tree manipulation code
        private void LoadResourceNodes(Descriptor Descriptor)
        {
            TreeNode ResTree = HierarchyTreeView.Nodes.Find("ResourcesTree", false).First();
            ResTree.Tag = new ResourceList(Descriptor.ResourcesList, new DirectoryInfo(
                Path.GetDirectoryName(DescriptorFileAddress) + Path.DirectorySeparatorChar + Descriptor.ResourceMainFolder)
                );

            //ResTree.Nodes.Clear();

            //foreach (Resource res in Descriptor.ResourcesList)
            //{
            //    AddResourceToTree(ResTree, res);
            //}
        }

        void UpdateResource(Resource NewResource, Resource OldResource)
        {
            ResourceGroupManager.Instance.RemoveResourceLocation(OldResource.Location);
            ResourceGroupManager.Instance.AddResourceLocation(
                NewResource.Location,
                NewResource.Type,
                NewResource.Group,
                NewResource.Recursive,
                false
                );

            if (NewResource.Group == OldResource.Group)
                ResourceGroupManager.Instance.InitializeResourceGroup(NewResource.Group);
            else
                ResourceGroupManager.Instance.InitializeAllResourceGroups();
        }

        private void LoadCameras()
        {
            //Loads all of the Cameras available in the scene in any level
            TreeNode LightNodesTree = HierarchyTreeView.Nodes.Find("CamerasTree", false)[0];
            LightNodesTree.Nodes.Clear();

            //for now we don't need to search all SceneNodes while all the cameras are available in RootSceneNode.Cameras
            foreach (Camera cam in _sceneManager.Cameras)
            {
                if (cam.Name == AgdCameraName)
                    continue;

                TreeNode objNode = new TreeNode();
                objNode.Tag = cam;
                objNode.Text = cam.Name;

                //TODO: each type of object can have it's own icon
                objNode.ImageKey =
                objNode.SelectedImageKey = typeof(Camera).Name;

                LightNodesTree.Nodes.Add(objNode);
            }
        }

        private void LoadLights()
        {
            //Loads all of the lights available in the scene in any level
            TreeNode LightNodesTree = HierarchyTreeView.Nodes.Find("LightsTree", false)[0];
            LightNodesTree.Nodes.Clear();

            //for now we don't need to search all SceneNodes while all the lights are available in _sceneManager.Lights
            foreach (Light lig in _sceneManager.Lights)
            {
                TreeNode objNode = new TreeNode();
                objNode.Tag = lig;
                objNode.Text = lig.Name;

                //TODO: each type of object can have it's own icon
                objNode.ImageKey =
                objNode.SelectedImageKey = typeof(Light).Name;

                LightNodesTree.Nodes.Add(objNode);
            }

        }

        private void LoadTerrainNodes()
        {
            try
            {
                TreeNode SceneNodesTree = HierarchyTreeView.Nodes.Find("TerrainNodesTree", true)[0];
                SceneNodesTree.Nodes.Clear();

                SceneNode tr = (SceneNode)_sceneManager.RootSceneNode.Children.First(a => a.Name == "TerrainRoot");
                SceneNodesTree.Tag = tr;

                TreeNode[] nodes = GetSubSceneNodes(tr, null, "Terrain", null);
                if (nodes != null)
                    SceneNodesTree.Nodes.AddRange(nodes);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.Write("Load Terrain Nodes error: " + ex.Message);
            }
        }

        /// <summary>
        /// needs to be calles after LoadResourceNodes
        /// </summary>
        /// <param name="Descriptor"></param>
        private void LoadTerrainConfig()
        {
            if (!String.IsNullOrEmpty(descriptor.Terrain))
            {
                TreeNode ResTree = HierarchyTreeView.Nodes.Find("ResourcesTree", false).First();

                TerrainSceneManagerTerrainConfig trCfg =
                    new TerrainSceneManagerTerrainConfig((ResourceList)ResTree.Tag);

                Axiom.FileSystem.FileInfo TerrainFile =
                    ResourceGroupManager.Instance.FindResourceFileInfo(
                        ResourceGroupManager.Instance.FindGroupContainingResource(descriptor.Terrain),
                        descriptor.Terrain
                    ).First();

                trCfg.LoadFromXml(TerrainFile.Filename);

                HierarchyTreeView.Nodes.Find("TerrainConfig", true)[0].Tag = trCfg;
            }
        }

        private void LoadSceneNodesTree()
        {
            LoadSceneNodesTree(null);
        }

        private void LoadSceneNodesTree(TreeNode TreeNodeRoot)
        {
            if (TreeNodeRoot == null)
            {
                TreeNodeRoot = HierarchyTreeView.Nodes.Find("SceneNodesTree", false)[0];
                TreeNodeRoot.Tag = _sceneManager.RootSceneNode;
            }

            TreeNodeRoot.Nodes.Clear();

            List<string> exc = new List<string>(1);
            //we don't want TerrainRoot to be loaded in SceneNodes hierarchy
            exc.Add("TerrainRoot");
            exc.Add(AgdSceneNodeName);

            SceneNode root = (SceneNode)TreeNodeRoot.Tag;

            if (root.ChildCount > 0)
                TreeNodeRoot.Nodes.AddRange(GetSubSceneNodes(root, exc, "SceneNode", SceneNodeRightClickMenu));

            if (root.ObjectCount > 0)
                TreeNodeRoot.Nodes.AddRange(GetAttachedMovableObjects(root));

        }

        private TreeNode[] GetSubSceneNodes(SceneNode AxiomNode, List<string> Excludes, string ImageKey, ContextMenuStrip Menu)
        {
            List<TreeNode> SubNodes = new List<TreeNode>(AxiomNode.ChildCount);

            foreach (SceneNode AxSubNode in AxiomNode.Children)
            {
                if (Excludes == null || !Excludes.Contains(AxSubNode.Name))
                {
                    TreeNode nNode = new TreeNode(AxSubNode.Name);
                    nNode.Tag = AxSubNode;
                    nNode.ImageKey = ImageKey;
                    nNode.SelectedImageKey = ImageKey;
                    nNode.ContextMenuStrip = Menu;

                    if (AxSubNode.ChildCount > 0)
                        nNode.Nodes.AddRange(GetSubSceneNodes(AxSubNode, Excludes, ImageKey, Menu));

                    if (AxSubNode.ObjectCount > 0)
                        nNode.Nodes.AddRange(GetAttachedMovableObjects(AxSubNode));

                    SubNodes.Add(nNode);
                }

            }

            return SubNodes.ToArray();
        }

        private TreeNode[] GetAttachedMovableObjects(SceneNode AxSubNode)
        {
            return GetAttachedMovableObjects(AxSubNode, null);
        }

        private TreeNode[] GetAttachedMovableObjects(SceneNode AxSubNode, Type ObjectType)
        {
            List<TreeNode> objs = new List<TreeNode>(AxSubNode.ObjectCount);

            try
            {
                foreach (MovableObject entity in AxSubNode.Objects)
                {
                    if (ObjectType == null || typeof(Entity) == ObjectType)
                    {
                        TreeNode objNode = new TreeNode();
                        objNode.Tag = entity;
                        objNode.Text = entity.Name;

                        //TODO: each type of object can have it's own icon
                        objNode.ImageKey =
                        objNode.SelectedImageKey = typeof(Entity).Name;

                        objs.Add(objNode);
                    }
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.Write("GetAttachedMovableObjects error: " + ex.Message);
            }

            return objs.ToArray();
        }
        #endregion

        #region Render related functionalities
        void AxiomListener(object sender, FrameEventArgs e)
        {
            input.Capture();

            MoveMovableObject(e);
            RotateMovableObject(e);
            MoveCamera(e);
        }

        delegate void axiomRenderFrameDelegate();

        void axiomRenderFrame()
        {
            AxiomEngine.RenderOneFrame();
        }

        void RotateMovableObject(FrameEventArgs e)
        {
            if (LastNode == null || !(LastNode.Tag is SceneNode))
                return;

            SceneNode scNode = (SceneNode)LastNode.Tag;

            if (inRotation)
            {
                Vector3 nPos;
                int movement = input.RelativeMouseX + input.RelativeMouseY;

                switch (MovementRotationCurrentAxe)
                {
                    case MovementRotationAxe.X:
                        scNode.Rotate(new Vector3(1, 0, 0), movement, TransformSpace.Local);
                        break;

                    case MovementRotationAxe.Y:
                        scNode.Rotate(new Vector3(0, 1, 0), movement, TransformSpace.Local);
                        break;

                    case MovementRotationAxe.Z:
                        scNode.Rotate(new Vector3(0, 0, 1), -movement, TransformSpace.Local);
                        break;
                }

                if (input.IsKeyPressed(KeyCodes.Escape) || input.IsMousePressed(Axiom.Input.MouseButtons.Left))
                {
                    exitMovableObjectRotation(scNode);
                }
            }
            else if (!inMovement)
            {
                enterMovableObjectRotation(scNode, input);
            }
        }

        private void enterMovableObjectRotation(SceneNode scNode, InputReader input)
        {
            if (input.IsKeyPressed(KeyCodes.R))
            {
                if (input.IsKeyPressed(KeyCodes.X))
                {
                    System.Diagnostics.Debug.Write("Entering MovableObject rotation mode, around: X");
                    MovementRotationCurrentAxe = MovementRotationAxe.X;
                }
                else if (input.IsKeyPressed(KeyCodes.Y))
                {
                    System.Diagnostics.Debug.Write("Entering MovableObject rotation mode, around: Y");
                    MovementRotationCurrentAxe = MovementRotationAxe.Y;
                }
                else if (input.IsKeyPressed(KeyCodes.Z))
                {
                    System.Diagnostics.Debug.Write("Entering MovableObject rotation mode, around: Z");
                    MovementRotationCurrentAxe = MovementRotationAxe.Z;
                }
                else
                    return;

                //add some boundingbox or something similar to show the movement situation
                WireBoundingBox wbBox = new WireBoundingBox();
                wbBox.Name = MovementIndicatorName;
                AxisAlignedBox a = new AxisAlignedBox();
                foreach (MovableObject obj in scNode.Objects)
                    a.Merge(obj.BoundingBox);

                wbBox.BoundingBox = a;
                scNode.AttachObject(wbBox);

                inRotation = true;
            }
        }

        private void exitMovableObjectRotation(SceneNode scNode)
        {
            scNode.WireBoundingBox = null;

            inRotation = false;
            System.Diagnostics.Debug.Write("Exiting MovableObject rotation mode.");
        }

        void MoveMovableObject(FrameEventArgs e)
        {
            if (LastNode == null || !(LastNode.Tag is SceneNode))
                return;

            SceneNode scNode = (SceneNode)LastNode.Tag;

            if (inMovement)
            {
                Vector3 nPos;
                int movement = input.RelativeMouseX + input.RelativeMouseY;

                switch (MovementRotationCurrentAxe)
                {
                    case MovementRotationAxe.X:
                        nPos = new Vector3(
                            scNode.Position.x + movement,
                            scNode.Position.y,
                            scNode.Position.z);

                        scNode.Position = nPos;

                        break;

                    case MovementRotationAxe.Y:
                        nPos = new Vector3(
                            scNode.Position.x,
                            scNode.Position.y - movement,
                            scNode.Position.z);

                        scNode.Position = nPos;

                        break;

                    case MovementRotationAxe.Z:
                        nPos = new Vector3(
                            scNode.Position.x,
                            scNode.Position.y,
                            scNode.Position.z + movement);

                        scNode.Position = nPos;

                        break;
                }

                if (input.IsKeyPressed(KeyCodes.Escape) || input.IsMousePressed(Axiom.Input.MouseButtons.Left))
                {
                    exitMovableObjectMovement(scNode);
                }
            }
            else if (!inRotation)
            {
                enterMovableObjectMovement(scNode, input);
            }
        }

        private void enterMovableObjectMovement(SceneNode scNode, InputReader input)
        {
            if (input.IsKeyPressed(KeyCodes.G))
            {
                if (input.IsKeyPressed(KeyCodes.X))
                {
                    System.Diagnostics.Debug.Write("Entering MovableObject movement mode, direction: X");
                    MovementRotationCurrentAxe = MovementRotationAxe.X;
                }
                else if (input.IsKeyPressed(KeyCodes.Y))
                {
                    System.Diagnostics.Debug.Write("Entering MovableObject movement mode, direction: Y");
                    MovementRotationCurrentAxe = MovementRotationAxe.Y;
                }
                else if (input.IsKeyPressed(KeyCodes.Z))
                {
                    System.Diagnostics.Debug.Write("Entering MovableObject movement mode, direction: Z");
                    MovementRotationCurrentAxe = MovementRotationAxe.Z;
                }
                else
                    return;

                //add some boundingbox or something similar to show the movement situation
                WireBoundingBox wbBox = new ColorWireBoundingBox(ColorEx.Red);
                wbBox.Name = MovementIndicatorName;
                AxisAlignedBox a = new AxisAlignedBox();
                foreach (MovableObject obj in scNode.Objects)
                    a.Merge(obj.BoundingBox);

                wbBox.BoundingBox = a;
                scNode.WireBoundingBox = wbBox;

                inMovement = true;
            }
        }

        private void exitMovableObjectMovement(SceneNode scNode)
        {
            scNode.WireBoundingBox = null;

            inMovement = false;
            System.Diagnostics.Debug.Write("Exiting MovableObject movement mode.");
        }

        void AxiomRenderLoop(object sender, System.Timers.ElapsedEventArgs e)
        {
            try
            {
                ((System.Timers.Timer)sender).Enabled = false;

                _renderWindow.IsActive = true;
                this.Invoke(new axiomRenderFrameDelegate(axiomRenderFrame));

            }
            finally
            {
                ((System.Timers.Timer)sender).Enabled = true;
            }
        }

        private void MoveCamera(FrameEventArgs e)
        {
            SceneNode camAndCosysNode = _sceneManager.GetSceneNode(AgdCameraAndCoorSysNodeName);
            SceneNode cosysNode = _sceneManager.GetSceneNode(AgdCoorSysNodeName);

            // ----> Rotation
            if (input.IsMousePressed(Axiom.Input.MouseButtons.Middle))
            {
                camAndCosysNode.Rotate(new Vector3(0, 1, 0), -input.RelativeMouseX * .3f, TransformSpace.World);

                camAndCosysNode.Rotate(new Vector3(1, 0, 0), -input.RelativeMouseY * .3f, TransformSpace.Local);

                cosysNode.Rotate(new Vector3(0, 1, 0), input.RelativeMouseX * .3f, TransformSpace.World);

                cosysNode.Rotate(new Vector3(1, 0, 0), input.RelativeMouseY * .3f, TransformSpace.Local);
            }

            // ----> Zooming
            if (input.RelativeMouseZ != 0)
            {
                Quaternion camDir = _sceneManager.GetSceneNode(AgdCameraNodeName).DerivedOrientation;

                camAndCosysNode.Translate(  
                    new Vector3(
                        -input.RelativeMouseZ * camDir.ZAxis.x,
                        -input.RelativeMouseZ * camDir.ZAxis.y,
                        -input.RelativeMouseZ * camDir.ZAxis.z
                        ),
                        TransformSpace.World
                    );
            }

            // ----> Keyboard movements
            //create new translation
            Vector3 translate = Vector3.Zero;
            if (input.IsKeyPressed(KeyCodes.LeftControl))
            {
                if (input.IsKeyPressed(KeyCodes.Up)) translate.z -= movementSpeed;
                if (input.IsKeyPressed(KeyCodes.Left)) translate.x -= movementSpeed;
                if (input.IsKeyPressed(KeyCodes.Down)) translate.z += movementSpeed;
                if (input.IsKeyPressed(KeyCodes.Right)) translate.x += movementSpeed;

                if (input.IsKeyPressed(KeyCodes.Space))
                    translate.y += input.IsKeyPressed(KeyCodes.LeftShift) ? -movementSpeed : movementSpeed;

                velocity += translate;
            }

            camAndCosysNode.Position += (velocity * e.TimeSinceLastFrame);

            //slow down if not moving
            if (translate == Vector3.Zero)
                velocity *= (1 - (3 * e.TimeSinceLastFrame));
        }
        #endregion

        private void HierarchyTreeView_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if (LastNode != null && LastNode != e.Node)
            {
                //moved away from terrain config node, we save the config and apply it to the scene
                if (LastNode.Tag is TerrainSceneManagerTerrainConfig)
                {
                    MessageBox.Show("Going to reload the terrain.", "Info", MessageBoxButtons.OK);
                    SaveAndReloadTerrainConfig();

                }
                else if (LastNode.Tag is SceneNode)
                {
                    if (inMovement)
                        exitMovableObjectMovement((SceneNode)LastNode.Tag);

                    ((SceneNode)LastNode.Tag).ShowBoundingBox = false;
                }
            }

            if (e.Node.Tag is SceneNode)
                ((SceneNode)e.Node.Tag).ShowBoundingBox = true;

            //display selected object's type somewhere
            ObjectTypeLbl.Text = e.Node.Tag != null ? e.Node.Tag.GetType().ToString() : "";
            PropertyViewer.SelectedObject = e.Node.Tag;

            LastNode = e.Node;
        }

        private void SaveAndReloadTerrainConfig()
        {
            RenderLoopTimer.Enabled = false;

            TerrainSceneManagerTerrainConfig terCfg = (TerrainSceneManagerTerrainConfig)LastNode.Tag;
            TreeNode ResTree = HierarchyTreeView.Nodes.Find("ResourcesTree", false).First();
            ResourceList resList = (ResourceList)ResTree.Tag;
            terCfg.SaveToXml(
                resList.GetGeneralResource().Location + Path.DirectorySeparatorChar + descriptor.Terrain);

            ResourceGroupManager.Instance.InitializeResourceGroup(resList.GetGeneralResource().Group);

            SceneNode trScNode = (SceneNode)_sceneManager.RootSceneNode.Children.First(a => a.Name == "TerrainRoot");
            Node[] terNodes = new Node[trScNode.Children.Count];
            trScNode.Children.CopyTo(terNodes, 0);
            foreach (SceneNode subNode in terNodes)
                _sceneManager.DestroySceneNode(subNode);

            _sceneManager.DestroySceneNode(trScNode);

            _sceneManager.LoadWorldGeometry(descriptor.Terrain);

            RenderLoopTimer.Enabled = true;
        }

        private void newMovableObjectMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                SceneNode AttachNode = (SceneNode)HierarchyTreeView.SelectedNode.Tag;
                Type objType = (System.Type)((ToolStripMenuItem)sender).Tag;

                //add an movable object to selected scene node
                Axiom.Collections.NamedParameterList Params = new Axiom.Collections.NamedParameterList();

                //Decide which params are needed based on the type
                //TODO: needed params can be defined in an XML file
                if (objType == typeof(Entity))
                {
                    Params.Add("mesh", "Prefab_Cube");
                }

                //find the next available name
                string NewName = objType.Name;
                int counter = 0;
                while (_sceneManager.HasMovableObject(NewName + counter, EntityFactory.TypeName))
                    counter++;

                MovableObject mvo = _sceneManager.CreateMovableObject(NewName + counter, objType.Name, Params);
                AttachNode.AttachObject(mvo);

                //TODO: move camera to newly added object

                System.Diagnostics.Debug.Write("New MovableObject of type " + objType.Name + " added: " + NewName + counter);

                LoadSceneNodesTree(HierarchyTreeView.SelectedNode);

                //some special types are kept alone for ease of access too:
                if (objType == typeof(Light))
                    LoadLights();
                else if (objType == typeof(Camera))
                    LoadCameras();
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.Write("Add MovableObject error: " + ex.Message);
            }
        }

        private void newSubSceneNodeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AddSceneNode addDlg = new AddSceneNode();
            
            if (addDlg.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    SceneNode MotherNode = (SceneNode)HierarchyTreeView.SelectedNode.Tag;
                    MotherNode.CreateChildSceneNode(addDlg.NodeName);

                    LoadSceneNodesTree(HierarchyTreeView.SelectedNode);

                    System.Diagnostics.Debug.Write("New SceneNode added: " + addDlg.NodeName);
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.Write("Add SceneNode error: " + ex.Message);
                }
            }
        }

        private void attachedObjectsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SceneNode selNode = (SceneNode)HierarchyTreeView.SelectedNode.Tag;
            foreach (MovableObject mvo in selNode.Objects)
                _sceneManager.DestroyMovableObject(mvo);

            selNode.DetachAllObjects();

            System.Diagnostics.Debug.Write("All objects deleted from node: " + selNode.Name);

            LoadSceneNodesTree(HierarchyTreeView.SelectedNode);
        }

        private void deleteToolStripMenuItem_DropDownOpening(object sender, EventArgs e)
        {
            sceneNodeAndObjectsToolStripMenuItem.Enabled =
                !(HierarchyTreeView.SelectedNode.Name == "SceneNodesTree");

            thisAttachedObjectToolStripMenuItem.DropDownItems.Clear();

            foreach (MovableObject mvo in ((SceneNode)HierarchyTreeView.SelectedNode.Tag).Objects)
            {
                ToolStripItem dItem =
                   thisAttachedObjectToolStripMenuItem.DropDownItems.Add(mvo.Name);
                dItem.Tag = mvo;
                dItem.Click += DeleteAttachedItem_Click;
            }

            attachedObjectsToolStripMenuItem.Enabled =
            thisAttachedObjectToolStripMenuItem.Enabled =
                !(thisAttachedObjectToolStripMenuItem.DropDownItems.Count <= 0);

        }

        void DeleteAttachedItem_Click(object sender, EventArgs e)
        {
            try
            {
                MovableObject mvo = (MovableObject)((ToolStripItem)sender).Tag;
                mvo.DetachFromParent();

                _sceneManager.DestroyMovableObject(mvo);

                System.Diagnostics.Debug.Write("Object deleted: " + mvo.Name);

                LoadSceneNodesTree(HierarchyTreeView.SelectedNode);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.Write("Object delete error: " + ex.Message);
            }
        }

        private void sceneNodeAndObjectsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SceneNode delNode = (SceneNode)HierarchyTreeView.SelectedNode.Tag;
            delNode.Parent.RemoveChild(delNode.Name);

            foreach (MovableObject mvo in delNode.Objects)
                _sceneManager.DestroyMovableObject(mvo);

            _sceneManager.DestroySceneNode(delNode);

            System.Diagnostics.Debug.Write("SceneNode and all its objects deleted: " + delNode.Name);

            LoadSceneNodesTree(HierarchyTreeView.SelectedNode.Parent);
        }

        private void HierarchyTreeView_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                HierarchyTreeView.SelectedNode = HierarchyTreeView.GetNodeAt(e.X, e.Y);
            }
        }

        private void addToolStripMenuItem_DropDownOpening(object sender, EventArgs e)
        {
            AddMovableObjectMenuItem.DropDownItems.Clear();

            foreach (System.Type mvo in PluginManager.MovableObjects)
            {
                ToolStripMenuItem mnu = new ToolStripMenuItem(mvo.Name);
                mnu.Tag = mvo;
                mnu.Click += newMovableObjectMenuItem_Click;
                AddMovableObjectMenuItem.DropDownItems.Add(mnu);
            }
        }

        private void refreshToolStripMenuItem_Click(object sender, EventArgs e)
        {
            LoadSceneNodesTree(HierarchyTreeView.SelectedNode);
        }

        private void refreshLightsToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            LoadLights();
        }

        private void refreshCamerasToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            LoadCameras();
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveProject(DescriptorFileAddress);
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            RenderLoopTimer.Enabled = false;
            this.Close();
        }

        private void RenderSystemRightClickMenu_Opening(object sender, CancelEventArgs e)
        {
            switchRenderSystemToolStripMenuItem.DropDownItems.Clear();
            foreach (string rsKey in AxiomEngine.RenderSystems.Keys)
            {
                ToolStripItem mnu = switchRenderSystemToolStripMenuItem.DropDownItems.Add(rsKey);
                mnu.Click += switchRenderSystemMenuItem_Click;
            }
        }

        private void switchRenderSystemMenuItem_Click(object sender, EventArgs e)
        {
            //if (MessageBox.Show("For this to work we have to save the options and restart the editor.\nAre you sure to do that?", "Info", MessageBoxButtons.YesNo)
            //    == DialogResult.Yes)
            {
                //TODO: save RS, restart
                RenderLoopTimer.Enabled = false;

                descriptor.RenderSystemKey = ((ToolStripItem)sender).Text;
                AxiomEngine.RenderSystem.Shutdown();
                AxiomEngine.RenderSystem = AxiomEngine.RenderSystems[descriptor.RenderSystemKey];
                _renderWindow = AxiomEngine.RenderSystem.Initialize(true, "3D Window");

                RenderLoopTimer.Enabled = true;
            }
        }

        private void applyTerrainConfigChangesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveAndReloadTerrainConfig();
        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveFileDialog sd = new SaveFileDialog();
            sd.Filter = "XML file (*.xml)|*.xml";
            if (sd.ShowDialog() == DialogResult.OK)
            {
                SaveProject(sd.FileName);
                DescriptorFileAddress = sd.FileName;
            }
        }

        private void reinitializeAllResourcesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ResourceGroupManager.Instance.InitializeAllResourceGroups();
        }
    }
}
